use std::collections::HashMap;
use std::env::args;
use std::sync::Arc;
use super::Input;
use wd_tools::sync::WaitGroup;
use crate::{Cmd,Flags};
use crate::interface::{Task, TaskInfo};

#[derive(Default)]
pub struct Application {
    desc: String,
    info : Vec<TaskInfo>,
    tasks : HashMap<String,Arc<dyn Task>>
}

impl Application {
    pub fn new(desc:impl ToString)->Self{
        Self{desc:desc.to_string(),
            ..Default::default()
        }
    }
    pub fn register_task<T:Task + 'static>(mut self, task:T) ->Self{
        let info = task.info();
        let name = info.name.clone();
        self.info.push(info);
        self.tasks.insert(name,Arc::new(task));self
    }
    fn build(&self,flags:Flags) ->Vec<(Arc<dyn Task>,Input)>{
        let mut tasks = vec![];
        for i in flags.cmds.iter() {
            let task = match self.tasks.get(i.cmd.as_str()){
                None => { wd_log::log_panic!("not found cmd[{}] handler",i.cmd.as_str())},
                Some(s)=>s.clone(),
            };
            let input = Application::build_input(i, &flags,task.info());
            tasks.push((task,input));
        }
        tasks
    }
    pub fn show(self){
        println!("description:{}",self.desc);
        println!("format:");
        let app_name = args().next().unwrap_or("application".to_string());
        println!("{} [OPTION] [[ARGS]]",app_name);
        for i in self.info.iter() {
            println!("  {} {}",i.name,i.help);
            for (arg,def,des) in i.flags.iter() {
                println!("      {} : {}. default[{}]",arg,des,def);
            }
        }
    }
    pub async fn run(self){

        let flags = Flags::parse_args();
        //如果没有任务需要执行则打印运行参数
        if flags.cmds.is_empty() {
            self.show();
            return;
        }

        let tasks = self.build(flags);
        let wg = WaitGroup::default();

        for (t,i) in tasks.iter(){
            let task = t.clone();
            let input = i.clone();
            wg.defer(move ||async move{
                if let Err(e) = task.run(input).await {
                    wd_log::log_error_ln!("run failed:{:?}",e);
                }
            })
        }

        Application::wait_exit_sigle(wg).await;

        for (t,i) in tasks.into_iter().rev() {  //倒序一个一个退出
            if let Err(e) = t.exit(i).await{
                wd_log::log_error_ln!("exit error:{:?}",e);
            }
        }
        wd_log::log_info_ln!("process exit success")
    }

    async fn wait_exit_sigle(wg:WaitGroup){
        let exit = async {
            if let Err(e) = tokio::signal::ctrl_c().await {
                wd_log::log_error_ln!("lister exit sigle error:{:?}",e);
            }
        };
        tokio::select! {
            _= exit =>{
                wd_log::log_debug_ln!("listed exit sigle, start quit process");
            }
            _= wg.wait() =>{
                wd_log::log_debug_ln!("all task over, start quit process");
            }
        }
    }
    fn build_input(cmd:&Cmd,flags:&Flags,info:TaskInfo)-> Input {
        let mut ctx = Input::default();
        for (k,v) in flags.args.iter() {
            ctx.store(k,v);
        }
        for (k,v,_) in info.flags.iter() {
            ctx.store(k,v);
        }
        for (k,v) in cmd.args.iter() {
            ctx.store(k,v);
        }
        return ctx
    }
}